﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpCart.BusinessObjects
{
    /// <summary>
    /// This class provides a wrapper to the database user class.
    /// It enforces business rules such as the validity of the email address,
    /// and allows consumers to save their changes to the database.
    /// </summary>
    public class User
    {
        #region Declarations
        Data.SharpDb Db;
        Data.User DataUser;
        bool IsNew;
        #endregion

        #region Properties
        /// <summary>
        /// The email address for the user.  Must be a valid email address.
        /// </summary>
        public string EmailAddress
        {
            get
            {
                return DataUser.EmailAddress;
            }
            set
            {
                string EmailRegex = @"^(([^<>()[\]\\.,;:\s@\""]+"
                        + @"(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@"
                        + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
                        + @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
                        + @"[a-zA-Z]{2,}))$";
                System.Text.RegularExpressions.Regex Regex = new System.Text.RegularExpressions.Regex(EmailRegex);
                if (!Regex.IsMatch(EmailAddress))
                {
                    throw new ArgumentException("Invalid email address.");
                }
                else
                {
                    DataUser.EmailAddress = value;
                }
            }
        }
        /// <summary>
        /// The user's password.
        /// </summary>
        public string Password
        {
            set
            {
                Random Rng = new Random();
                DataUser.Nonce = Rng.Next();
                DataUser.Password = Utils.Security.Encryption.HashPassword(value, DataUser.Nonce );
            }
        }

        /// <summary>
        /// Indicates whether the user is currently active.
        /// </summary>
        public bool IsActive
        {
            get
            {
                return DataUser.IsActive;
            }
            set
            {
                DataUser.IsActive = value;
            }
        }

        /// <summary>
        /// The user's user type.
        /// </summary>
        public UserType Type
        {
            get
            {
                if (!DataUser.UserTypeReference.IsLoaded)
                {
                    DataUser.UserTypeReference.Load();
                }
                return new UserType(DataUser.UserType, Db);
            }
            set
            {
                DataUser.UserType = Db.UserType.First(U => U.Type == value.Type);
            }
        }

        public List<CreditCard> CreditCards
        {
            get
            {
                if (!DataUser.CreditCards.IsLoaded)
                {
                    DataUser.CreditCards.Load();
                }
                List<CreditCard> Result = new List<CreditCard>();
                foreach (Data.UserCreditCard Card in DataUser.CreditCards)
                {
                    Result.Add(CreditCard.GetCard(Card, Db));
                }
                return Result;
            }
        }
        public List<UserAddress> Addresses
        {
            get
            {
                if (!DataUser.Addresses.IsLoaded)
                {
                    DataUser.Addresses.Load();
                }
                List<UserAddress> Result = new List<UserAddress>();
                foreach (Data.UserAddress Address in DataUser.Addresses)
                {
                    Result.Add(new UserAddress(Db, Address));
                }
                Result.Sort();
                return Result;
            }
        }
        public List<UserAddress> ActiveAddresses
        {
            get
            {
                List<UserAddress> Result = new List<UserAddress>();
                foreach (UserAddress Address in Addresses)
                {
                    if (Address.IsActive)
                    {
                        Result.Add(Address);
                    }
                }
                return Result;
            }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Use this constructor to create new users.
        /// </summary>
        public User()
        {
            Db = new SharpCart.Data.SharpDb();
            DataUser = new SharpCart.Data.User();
            IsNew = true;
        }

        /// <summary>
        /// Use this constructor to load an existing user.
        /// </summary>
        /// <param name="EmailAddress">The email address for the existing user</param>
        public User(string EmailAddress)
        {
            Db = new SharpCart.Data.SharpDb();
            var DataUsers = from U in Db.User where U.EmailAddress == EmailAddress select U;
            if (DataUsers.Count() == 0)
            {
                throw new ArgumentException("Invalid username", "EmailAddress");
            }
            else
            {
                DataUser = DataUsers.First();
            }
            IsNew = false;
        }

        /// <summary>
        /// Use this constructor when you have access to a data user and a db context.
        /// </summary>
        /// <param name="DataUser">The data user to use.</param>
        /// <param name="Db">The db context to use</param>
        internal User(Data.User DataUser, SharpCart.Data.SharpDb Db)
        {
            this.DataUser = DataUser;
            this.Db = Db;
            IsNew = false;
        }
        #endregion

        #region Instance Methods
        /// <summary>
        /// Creates a pseudo-random session id for a user if the password supplied matches the password stored for the user.
        /// </summary>
        /// <param name="TestPassword">The password to test.</param>
        /// <returns>A pseudo-random session id for the user.</returns>
        public virtual string GetSessionID(string TestPassword)
        {
            string Result;
            if (IsValidPassword(TestPassword) && IsActive)
            {
                Random RandomGen = new Random();
                double Random = RandomGen.NextDouble();
                Result = Utils.Security.Encryption.HashString(Random + EmailAddress + TestPassword + Random);
            }
            else
            {
                throw new ArgumentException("Invalid login credentials", "Password");
            }
            return Result;
        }
        
        /// <summary>
        /// Saves the current state of the user to the database.
        /// </summary>
        public virtual void Save()
        {
            if (IsNew)
            {
                Db.AddToUser(DataUser);
            }
            Db.SaveChanges();
            IsNew = false;
        }


        /// <summary>
        /// Checks to see if the supplied password matches the stored password
        /// </summary>
        /// <param name="TestPassword">The password to test</param>
        /// <returns>True if the password matches, false otherwise.</returns>
        protected virtual bool IsValidPassword(string TestPassword)
        {
            return Utils.Security.Encryption.HashString(TestPassword) == DataUser.Password;
        }
        #endregion

        #region Static Methods
        public static List<User> GetUsers()
        {
            List<User> Result = new List<User>();
            Data.SharpDb Db = new SharpCart.Data.SharpDb();
            var UserList = from U in Db.User select U;
            foreach(Data.User DataUser in UserList)
            {
                Result.Add(new User(DataUser, Db));
            }
            return Result;
        }
        public static List<User> GetActiveUsers()
        {
            List<User> Result = new List<User>();
            Data.SharpDb Db = new SharpCart.Data.SharpDb();
            var UserList = from U in Db.User where U.IsActive select U;
            foreach (Data.User DataUser in UserList)
            {
                Result.Add(new User(DataUser, Db));
            }
            return Result;
        }
        #endregion
    }
}
